Skip to content

fix: correct workspace admin permission validation in project member updates#9119

Open
KanteshMurade wants to merge 3 commits into
makeplane:previewfrom
KanteshMurade:clean-role-fix
Open

fix: correct workspace admin permission validation in project member updates#9119
KanteshMurade wants to merge 3 commits into
makeplane:previewfrom
KanteshMurade:clean-role-fix

Conversation

@KanteshMurade
Copy link
Copy Markdown
Contributor

@KanteshMurade KanteshMurade commented May 23, 2026

Description

Fixes issue #9109 where workspace admins could not promote project members to Admin if their own project role was lower.

The backend permission validation in ProjectMemberViewSet.partial_update was incorrectly checking the workspace role of the target member being edited instead of the requesting user.

This change updates the validation logic to use the requesting user's workspace role for permission checks.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Screenshots and Media (if applicable)

N/A

Test Scenarios

  • Verified workspace admins can assign higher project roles even if their own project role is lower.
  • Verified non-admin project members still cannot assign roles higher than their own.
  • Reviewed backend permission flow for requester vs target role validation.

References

Fixes #9109

Summary by CodeRabbit

  • Tests
    • Added contract tests for project member role-update authorization. Tests verify proper access controls for role promotions, ensuring appropriate capabilities for different permission levels while non-admin members receive proper restrictions, and validate database consistency following authorization checks across multiple role promotion scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

This PR adds contract tests for project member role-update authorization. A test class with a URL helper is introduced, followed by three test cases validating that workspace admins can promote members and non-admins receive 403 Forbidden responses when attempting role promotions.

Changes

Project member role-update contract tests

Layer / File(s) Summary
Test class and URL helper
apps/api/plane/tests/contract/app/test_project_app.py
ROLE constant is imported, and TestProjectMemberAPI class is introduced with get_project_member_url() helper method to construct PATCH endpoint URLs for project member role updates.
Authorization tests for role updates
apps/api/plane/tests/contract/app/test_project_app.py
Three contract tests verify that workspace admins can update another member's role to a higher level (with both requester and target roles confirmed after refresh), non-admin members receive 403 Forbidden when promoting to ADMIN, and non-admin members receive 403 Forbidden when promoting lower-role members—each confirming the target role remains unchanged in the database.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Three tests now bloom where admins reign,
They validate role—no grant in vain,
Workspace lords promote with grace,
While lesser roles stay in their place. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: correcting workspace admin permission validation in project member updates.
Description check ✅ Passed The description covers required sections: clear problem statement, type of change (bug fix), test scenarios, and references to the fixed issue (#9109).
Linked Issues check ✅ Passed The PR directly addresses issue #9109 by fixing the permission validation logic allowing workspace admins to assign higher project roles.
Out of Scope Changes check ✅ Passed All changes are focused on adding contract tests for the corrected project member role update permissions, directly supporting the linked issue fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/plane/app/views/project/member.py (2)

209-230: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Duplicate workspace role queries with inconsistent variable names.

The code fetches target_workspace_role and the requester's workspace role twice:

  1. Lines 211-218: requester_workspace_role / is_workspace_admin
  2. Lines 220-229: requesting_workspace_role / is_requesting_workspace_admin

This causes redundant database queries and creates confusion about which variables to use downstream. The HEAD conflict block uses is_workspace_admin while the other block uses is_requesting_workspace_admin.

After resolving the merge conflict, keep only one set of these queries with consistent naming.

Proposed fix (after resolving merge conflict)
-
 # Fetch the target's workspace role (used to cap the new project role)
 target_workspace_role = WorkspaceMember.objects.get(
     workspace__slug=slug, member=project_member.member, is_active=True
 ).role
 # Fetch the requester's workspace role to decide if they may bypass project-role checks
 requester_workspace_role = WorkspaceMember.objects.get(
     workspace__slug=slug, member=request.user, is_active=True
 ).role
 is_workspace_admin = requester_workspace_role == ROLE.ADMIN.value
-
-        # Fetch the workspace role of the project member
-        target_workspace_role = WorkspaceMember.objects.get(
-            workspace__slug=slug, member=project_member.member, is_active=True
-        ).role
-
-        # Fetch the workspace role of the requesting user for permission checks
-        requesting_workspace_role = WorkspaceMember.objects.get(
-            workspace__slug=slug, member=request.user, is_active=True
-        ).role
-        is_requesting_workspace_admin = requesting_workspace_role == ROLE.ADMIN.value
-         
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/app/views/project/member.py` around lines 209 - 230, Remove
the duplicated WorkspaceMember queries and normalize variable names: keep a
single fetch for the target's workspace role into target_workspace_role (from
WorkspaceMember with workspace__slug=slug and member=project_member.member) and
a single fetch for the requesting user's workspace role into
requester_workspace_role (from WorkspaceMember with workspace__slug=slug and
member=request.user), then compute is_workspace_admin = requester_workspace_role
== ROLE.ADMIN.value and use those variables throughout (replace any uses of
requesting_workspace_role or is_requesting_workspace_admin with
requester_workspace_role and is_workspace_admin) to eliminate redundant DB calls
and make naming consistent.

246-293: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Unresolved merge conflict markers will cause syntax errors.

The file contains literal merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> c31299f8dd) that must be resolved before this code can run. Python will raise a SyntaxError when attempting to parse this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/app/views/project/member.py` around lines 246 - 293, Resolve
the literal merge markers by removing the conflict block and keeping a single
coherent role-validation flow: delete the lines from <<<<<<< HEAD through
======= and the >>>>>>> marker, and retain the consolidated checks that first
validate workspace limits using target_workspace_role and
int(request.data.get("role", project_member.role)) against [15, 20], then
validate the requester’s authority by comparing int(request.data.get("role",
project_member.role)) to requested_project_member.role and using the consistent
boolean name is_requesting_workspace_admin (or rename uses of is_workspace_admin
to match). Ensure the code uses the same variable names (project_member vs
requested_project_member) consistently, converts request.data role to int once,
and returns the appropriate Response/status as in the consolidated version.
🧹 Nitpick comments (1)
apps/api/plane/tests/contract/app/test_project_app.py (1)

239-261: 💤 Low value

Test relies on implicit workspace admin setup from fixtures.

The test assumes create_user is a workspace admin (role 20) through the workspace fixture, but this isn't explicit in the test. If the fixture behavior changes, this test could fail unexpectedly.

Consider adding an explicit assertion or comment documenting this fixture dependency:

# create_user is a workspace admin (role=20) via the workspace fixture

Alternatively, verify the workspace membership explicitly at the start of the test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/tests/contract/app/test_project_app.py` around lines 239 -
261, The test test_workspace_admin_can_promote_member_above_project_role relies
implicitly on the create_user being a workspace admin via the workspace fixture;
make that dependency explicit by asserting or ensuring the workspace
membership/role at the start of the test (e.g., verify WorkspaceMember for
create_user has role 20) or add a one-line comment documenting that create_user
is a workspace admin; reference the test name
test_workspace_admin_can_promote_member_above_project_role, the create_user
fixture, and the WorkspaceMember/ProjectMember checks so maintainers can locate
and make the verification or comment change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/plane/tests/contract/app/test_project_app.py`:
- Around line 283-284: The test in
apps/api/plane/tests/contract/app/test_project_app.py asserts
response.data["error"] equals "You cannot update a role that is higher than your
own role", which is one side of an unresolved merge in member.py (the
alternative message is "You cannot assign a role equal to or higher than your
own"); update the test to match the final error string chosen in member.py (or
make the assertion resilient by checking for the canonical substring used in the
resolved message) so the assertion aligns with the actual message thrown by the
role-update validation.

---

Outside diff comments:
In `@apps/api/plane/app/views/project/member.py`:
- Around line 209-230: Remove the duplicated WorkspaceMember queries and
normalize variable names: keep a single fetch for the target's workspace role
into target_workspace_role (from WorkspaceMember with workspace__slug=slug and
member=project_member.member) and a single fetch for the requesting user's
workspace role into requester_workspace_role (from WorkspaceMember with
workspace__slug=slug and member=request.user), then compute is_workspace_admin =
requester_workspace_role == ROLE.ADMIN.value and use those variables throughout
(replace any uses of requesting_workspace_role or is_requesting_workspace_admin
with requester_workspace_role and is_workspace_admin) to eliminate redundant DB
calls and make naming consistent.
- Around line 246-293: Resolve the literal merge markers by removing the
conflict block and keeping a single coherent role-validation flow: delete the
lines from <<<<<<< HEAD through ======= and the >>>>>>> marker, and retain the
consolidated checks that first validate workspace limits using
target_workspace_role and int(request.data.get("role", project_member.role))
against [15, 20], then validate the requester’s authority by comparing
int(request.data.get("role", project_member.role)) to
requested_project_member.role and using the consistent boolean name
is_requesting_workspace_admin (or rename uses of is_workspace_admin to match).
Ensure the code uses the same variable names (project_member vs
requested_project_member) consistently, converts request.data role to int once,
and returns the appropriate Response/status as in the consolidated version.

---

Nitpick comments:
In `@apps/api/plane/tests/contract/app/test_project_app.py`:
- Around line 239-261: The test
test_workspace_admin_can_promote_member_above_project_role relies implicitly on
the create_user being a workspace admin via the workspace fixture; make that
dependency explicit by asserting or ensuring the workspace membership/role at
the start of the test (e.g., verify WorkspaceMember for create_user has role 20)
or add a one-line comment documenting that create_user is a workspace admin;
reference the test name
test_workspace_admin_can_promote_member_above_project_role, the create_user
fixture, and the WorkspaceMember/ProjectMember checks so maintainers can locate
and make the verification or comment change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4476ba48-3f71-4f22-aac5-1839bc709b6e

📥 Commits

Reviewing files that changed from the base of the PR and between 039d582 and 91e807d.

📒 Files selected for processing (2)
  • apps/api/plane/app/views/project/member.py
  • apps/api/plane/tests/contract/app/test_project_app.py

Comment thread apps/api/plane/tests/contract/app/test_project_app.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/plane/app/views/project/member.py (1)

247-253: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't block all non-admin project members from role edits.

Lines 249-253 still short-circuit every requester whose project role is below Admin, so the comparison-based checks below never run for members/guests. That means a project member cannot update someone to a lower role, and the new contract test in apps/api/plane/tests/contract/app/test_project_app.py will currently get 403 {"error": "You do not have permission to update roles"} instead of the intended “above your own role” denial.

Suggested minimal fix
         if "role" in request.data:
-            # Only Admins can modify roles
-            if requested_project_member.role < ROLE.ADMIN.value and not is_workspace_admin:
-                return Response(
-                    {"error": "You do not have permission to update roles"},
-                    status=status.HTTP_403_FORBIDDEN,
-                )
-
             # Cannot modify a member whose role is equal to or higher than your own
             if project_member.role >= requested_project_member.role and not is_workspace_admin:
                 return Response(
                     {"error": "You cannot update the role of a member with a role equal to or higher than your own"},
                     status=status.HTTP_403_FORBIDDEN,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/app/views/project/member.py` around lines 247 - 253, The
current check unconditionally blocks anyone below Admin from changing roles;
change it to only block unauthorized promotions or edits of admins: when "role"
in request.data, read the intended new role (new_role = request.data["role"])
and the requester's project role (e.g., requester_role), then if not
is_workspace_admin enforce: if requester_role < ROLE.ADMIN.value and new_role >
requester_role (attempting to promote above your role) OR if
requested_project_member.role >= ROLE.ADMIN.value and requester_role <
ROLE.ADMIN.value (non-admin trying to change an admin), return the 403; use ROLE
enum values for comparisons and keep requested_project_member.role and
is_workspace_admin checks in place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/api/plane/app/views/project/member.py`:
- Around line 247-253: The current check unconditionally blocks anyone below
Admin from changing roles; change it to only block unauthorized promotions or
edits of admins: when "role" in request.data, read the intended new role
(new_role = request.data["role"]) and the requester's project role (e.g.,
requester_role), then if not is_workspace_admin enforce: if requester_role <
ROLE.ADMIN.value and new_role > requester_role (attempting to promote above your
role) OR if requested_project_member.role >= ROLE.ADMIN.value and requester_role
< ROLE.ADMIN.value (non-admin trying to change an admin), return the 403; use
ROLE enum values for comparisons and keep requested_project_member.role and
is_workspace_admin checks in place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e16afcc6-902b-46d4-8a40-d6d9b9c086e5

📥 Commits

Reviewing files that changed from the base of the PR and between 91e807d and 9f8572c.

📒 Files selected for processing (2)
  • apps/api/plane/app/views/project/member.py
  • apps/api/plane/tests/contract/app/test_project_app.py

@KanteshMurade
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Member

@sriramveeraghanta sriramveeraghanta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR claims to fix #9109, but that bug was already fixed on preview via commit 4c1bdd1d62 (PR #9014), which made is_workspace_admin derive from the requester's workspace role. Tracing the test_workspace_admin_can_promote_member_above_project_role scenario against the base branch (no PR applied), the request already succeeds.

The new code on top introduces:

  • A regression at member.py:271 (workspace-role cap now runs outside if "role" in request.data, blocking non-role PATCHes on guests with elevated project roles).
  • Two duplicate WorkspaceMember.objects.get queries per call (lines 221, 226) — target_workspace_role and requesting_workspace_role are re-fetches of the variables already computed two lines above.
  • An unreachable 400 branch at lines 277–285 (strictly weaker than the existing >= guard above).
  • A silent permission expansion: removing the requested_project_member.role < ROLE.ADMIN.value guard lets non-admin project members modify guest project roles, where they were previously hard-blocked. Not mentioned in the PR description.
  • A failing test: test_project_member_cannot_promote_member_above_own_project_role will fail — the earlier project_member.role >= requested_project_member.role guard returns 403 with a different message before the new 400 branch is reached.

Recommend either closing in favor of preview's current state, or rewriting to delete the duplicate queries/dead checks, move the workspace-role cap back inside if "role" in request.data, fix the failing test, and explicitly confirm whether the admin-only role-modification gate should be removed.

Inline comments below.

is_workspace_admin = requester_workspace_role == ROLE.ADMIN.value

# Fetch the workspace role of the project member
target_workspace_role = WorkspaceMember.objects.get(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate query. Lines 211 (target_workspace_role) and 215 (requester_workspace_role) on the base branch already compute exactly this. Lines 221–229 re-fetch the same two WorkspaceMember rows and assign aliases (requesting_workspace_role, is_requesting_workspace_admin) for values that are bit-for-bit identical to requester_workspace_role / is_workspace_admin.

Net effect: 2 extra DB round-trips per partial_update and two pairs of synonymous variable names that will eventually diverge in someone's edit. Drop the duplicates and use the existing is_workspace_admin everywhere.

{"error": "You do not have permission to update roles"},
status=status.HTTP_403_FORBIDDEN,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent permission expansion. The deletion just above removed:

if requested_project_member.role < ROLE.ADMIN.value and not is_workspace_admin:
    return Response({"error": "You do not have permission to update roles"},
                    status=status.HTTP_403_FORBIDDEN)

That guard hard-blocked any non-admin requester from changing any role at all. Without it, a project MEMBER (role=15, not workspace admin) can now update a project GUEST's role — only the >= requested_project_member.role guard remains. Was this intentional? It's not mentioned in the PR description and isn't covered by either new test.

status=status.HTTP_400_BAD_REQUEST,
)

if target_workspace_role in [5] and int(request.data.get("role", project_member.role)) in [15, 20]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regression — runs outside if "role" in request.data. This guard is no longer scoped to role updates. With request.data.get("role", project_member.role), when role is not in the request body, it falls back to the existing project_member.role.

Concrete failure: project member with workspace_role=GUEST (5) and pre-existing project_role=MEMBER (15) — e.g. legacy data, or anyone whose workspace role was downgraded after project assignment. Any PATCH that touches only non-role fields (preferences, sort order, view props, etc.) now 400s with "You cannot add a user with role higher than the workspace role".

Move this check back inside the if "role" in request.data: block — note the identical check already exists there at line 265, so this block is also redundant when role is present.

Minor: prefer [ROLE.GUEST.value] / [ROLE.MEMBER.value, ROLE.ADMIN.value] over the magic numbers [5] and [15, 20].

status=status.HTTP_400_BAD_REQUEST,
)

if (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code. This 400 branch is unreachable:

  • For a non-workspace-admin requester, the existing check at line 258 (new_role >= requested_project_member.role and not is_workspace_admin) already returns 403 for anything strictly greater (>= subsumes >). It fires first.
  • For a workspace admin, is_workspace_admin == is_requesting_workspace_admin == True (same expression, computed twice), so not is_requesting_workspace_admin is False and this branch is skipped.

No input reaches the return Response(..., 400). Either delete it, or — if the intent was to surface a different error message — replace the earlier 403 instead of stacking another check after it.

workspace__slug=slug, member=request.user, is_active=True
).role
is_requesting_workspace_admin = requesting_workspace_role == ROLE.ADMIN.value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace on this line (and stray double-blank lines at 209, 246, 287). Whatever the project lints with (ruff/flake8/black) will almost certainly flag these.

"""Non-workspace-admin project members cannot assign roles above their own project role."""
project = Project.objects.create(name="Protected Role Project", identifier="PRP", workspace=workspace)

requesting_user = User.objects.create_user(email="requester@example.com", username="requester")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion will fail.

With requester project_role=15 patching a target whose project_role=15, the first guard in partial_update fires:

if project_member.role >= requested_project_member.role and not is_workspace_admin:
    return Response({"error": "You cannot update the role of a member with a role equal to or higher than your own"},
                    status=status.HTTP_403_FORBIDDEN)

15 >= 15 is True, not False is True → returns 403 with the equal-to-or-higher message. The new 400 branch this test targets (member.py:277) is unreachable (see comment there).

Both assert response.status_code == 400 and the error string assertion will fail. To make the assertion match the new branch's message, the scenario needs the requester's project_role to be strictly greater than new_role for both prior guards — which contradicts the test name. Either redesign the scenario or assert against the actual 403 + message that the existing guard returns.

requesting_project_member = ProjectMember.objects.create(
project=project, member=create_user, role=5, is_active=True
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test passes on preview without the PR. The bug it documents (workspace admin with low project role unable to promote) was already fixed by #9014 (is_workspace_admin = requester_workspace_role == ROLE.ADMIN.value). The test is still valuable as a regression guard, but it shouldn't be framed as proving this PR fixes anything — the production fix is already shipped.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/api/plane/tests/contract/app/test_project_app.py (1)

240-264: 💤 Low value

Consider making the workspace admin setup explicit.

The test relies on create_user being a workspace admin (via the session_client fixture setup) but doesn't explicitly establish this within the test body. Other tests in this file create workspace memberships explicitly (e.g., lines 578-579). While the test works as-is, adding an explicit WorkspaceMember.objects.create(workspace=workspace, member=create_user, role=ROLE.ADMIN.value, is_active=True) would make the test more self-documenting and reduce implicit fixture dependencies.

📝 Suggested enhancement for clarity
 def test_workspace_admin_can_promote_member_above_project_role(self, session_client, workspace, create_user):
     """Workspace admins can assign project roles above their own project role."""
     project = Project.objects.create(name="Role Project", identifier="RP", workspace=workspace)
+    # Explicitly set up create_user as workspace admin
+    WorkspaceMember.objects.create(
+        workspace=workspace, member=create_user, role=ROLE.ADMIN.value, is_active=True
+    )
     requesting_project_member = ProjectMember.objects.create(
         project=project, member=create_user, role=ROLE.GUEST.value, is_active=True
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/tests/contract/app/test_project_app.py` around lines 240 -
264, The test test_workspace_admin_can_promote_member_above_project_role relies
on an implicit fixture to make create_user a workspace admin; make this explicit
by creating a WorkspaceMember for create_user with role=ROLE.ADMIN.value and
is_active=True before performing the PATCH. Specifically, add a call to
WorkspaceMember.objects.create(workspace=workspace, member=create_user,
role=ROLE.ADMIN.value, is_active=True) near the start of the test (before the
request) so the test clearly documents the requester’s admin status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/api/plane/tests/contract/app/test_project_app.py`:
- Around line 240-264: The test
test_workspace_admin_can_promote_member_above_project_role relies on an implicit
fixture to make create_user a workspace admin; make this explicit by creating a
WorkspaceMember for create_user with role=ROLE.ADMIN.value and is_active=True
before performing the PATCH. Specifically, add a call to
WorkspaceMember.objects.create(workspace=workspace, member=create_user,
role=ROLE.ADMIN.value, is_active=True) near the start of the test (before the
request) so the test clearly documents the requester’s admin status.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ecc6ac48-649b-407e-ac51-6dbda872038c

📥 Commits

Reviewing files that changed from the base of the PR and between 08043ff and 9b8e33a.

📒 Files selected for processing (1)
  • apps/api/plane/tests/contract/app/test_project_app.py

@KanteshMurade
Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review.

After re-checking the current preview behavior and reviewing #9014, I realized the original issue had already been addressed and my additional permission logic changes were unnecessary.

I’ve revised the PR to focus only on regression coverage:

  • restored the existing permission logic in member.py;
  • removed duplicate queries and unreachable branches;
  • corrected the failing test expectation to match the current guard behavior;
  • added focused regression tests around workspace-admin promotion and non-admin permission boundaries.

The PR no longer changes backend permission behavior and is now intended only to validate and protect the current behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: You cannot assign a role equal to or higher than your own

2 participants